import pandas as pd
import altair as alt
import numpy as np
imdb_data = pd.read_csv('../data/imdb.csv')
imdb_data.head()
| Name | Date | Rate | Votes | Genre | Duration | Type | Certificate | Episodes | Nudity | Violence | Profanity | Alcohol | Frightening | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | No Time to Die | 2021 | 7.6 | 107,163 | Action, Adventure, Thriller | 163 | Film | PG-13 | - | Mild | Moderate | Mild | Mild | Moderate |
| 1 | The Guilty | 2021 | 6.3 | 64,375 | Crime, Drama, Thriller | 90 | Film | R | - | None | None | Severe | None | Moderate |
| 2 | The Many Saints of Newark | 2021 | 6.4 | 27,145 | Crime, Drama | 120 | Film | R | - | Moderate | Severe | Severe | Moderate | Moderate |
| 3 | Venom: Let There Be Carnage | 2021 | 6.4 | 30,443 | Action, Adventure, Sci-Fi | 97 | Film | PG-13 | - | None | Moderate | Moderate | Mild | Moderate |
| 4 | Dune | 2021 | 8.3 | 84,636 | Action, Adventure, Drama | 155 | Film | PG-13 | - | None | Moderate | None | Mild | Moderate |
film_imdb_data = imdb_data[(imdb_data['Type'] != 'Series') & (imdb_data['Rate'] != 'No Rate')].reset_index()
film_imdb_data.drop(['index', 'Episodes', 'Nudity', 'Violence', 'Profanity', 'Alcohol', 'Frightening'], axis = 1, inplace = True)
film_imdb_data['Rate'] = film_imdb_data['Rate'].astype(float)
film_imdb_data['Votes'] = film_imdb_data['Votes'].replace(',','', regex=True).astype(int)
film_imdb_data.columns = ['Name', 'start_date', 'rate', 'n_of_votes', 'genres', 'duration', 'type', 'certificate']
film_imdb_data['end_date'] = film_imdb_data['start_date']
film_imdb_data
| Name | start_date | rate | n_of_votes | genres | duration | type | certificate | end_date | |
|---|---|---|---|---|---|---|---|---|---|
| 0 | No Time to Die | 2021 | 7.6 | 107163 | Action, Adventure, Thriller | 163 | Film | PG-13 | 2021 |
| 1 | The Guilty | 2021 | 6.3 | 64375 | Crime, Drama, Thriller | 90 | Film | R | 2021 |
| 2 | The Many Saints of Newark | 2021 | 6.4 | 27145 | Crime, Drama | 120 | Film | R | 2021 |
| 3 | Venom: Let There Be Carnage | 2021 | 6.4 | 30443 | Action, Adventure, Sci-Fi | 97 | Film | PG-13 | 2021 |
| 4 | Dune | 2021 | 8.3 | 84636 | Action, Adventure, Drama | 155 | Film | PG-13 | 2021 |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 4296 | The Human Centipede II (Full Sequence) | 2011 | 3.8 | 37492 | Horror | 91 | Film | Not Rated | 2011 |
| 4297 | Double Indemnity | 1944 | 8.3 | 150448 | Crime, Drama, Film-Noir | 107 | Film | Passed | 1944 |
| 4298 | Before the Devil Knows You're Dead | 2007 | 7.3 | 100668 | Crime, Drama, Thriller | 117 | Film | R | 2007 |
| 4299 | Queen Bees | 2021 | 6.0 | 887 | Comedy, Drama, Romance | 100 | Film | PG-13 | 2021 |
| 4300 | Death Race | 2008 | 6.3 | 203578 | Action, Sci-Fi, Thriller | 105 | Film | R | 2008 |
4301 rows × 9 columns
series_data = pd.read_csv('../data/series_data.csv')
series_data.drop(['Poster_Link', 'Overview', 'Star1', 'Star2', 'Star3', 'Star4'], axis = 1, inplace = True)
series_data.columns = ['Name', 'series_runtime', 'certificate', 'episodes_runtime', 'genres', 'rate', 'n_of_votes']
series_data['type'] = 'Series'
series_data[['start_date', 'end_date']] = series_data['series_runtime'].str.split('–', expand=True)
series_data.drop(['series_runtime'], axis = 1, inplace = True)
series_data['start_date'] = series_data['start_date'].replace('\(', '', regex=True)
series_data['start_date'] = series_data['start_date'].replace('\)', '', regex=True)
series_data['start_date'] = series_data['start_date'].replace('I', '', regex=True)
series_data['start_date'] = series_data['start_date'].astype(int)
series_data['end_date'] = series_data['end_date'].replace('\)', '', regex=True)
series_data['end_date'] = series_data['end_date'].replace(np.nan, 2025)
series_data['end_date'] = series_data['end_date'].replace(' ', 2025)
series_data['end_date'] = series_data['end_date'].astype(int)
series_data
| Name | certificate | episodes_runtime | genres | rate | n_of_votes | type | start_date | end_date | |
|---|---|---|---|---|---|---|---|---|---|
| 0 | Game of Thrones | A | 57 min | Action, Adventure, Drama | 9.3 | 1773458 | Series | 2011 | 2019 |
| 1 | Breaking Bad | 18 | 49 min | Crime, Drama, Thriller | 9.5 | 1468887 | Series | 2008 | 2013 |
| 2 | The Walking Dead | 18+ | 44 min | Drama, Horror, Thriller | 8.2 | 854698 | Series | 2010 | 2025 |
| 3 | Friends | 13+ | 22 min | Comedy, Romance | 8.9 | 829816 | Series | 1994 | 2004 |
| 4 | Stranger Things | 15 | 51 min | Drama, Fantasy, Horror | 8.7 | 824966 | Series | 2016 | 2025 |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 1995 | Shaman Kingu | NaN | 23 min | Animation, Action, Adventure | 8.1 | 5131 | Series | 2001 | 2005 |
| 1996 | Eerie, Indiana | NaN | 30 min | Adventure, Comedy, Drama | 8.2 | 5128 | Series | 1991 | 1992 |
| 1997 | Gunsmoke | NaN | 60 min | Western | 7.9 | 5115 | Series | 1955 | 1975 |
| 1998 | The Cheat | NaN | 20 min | Action, Drama, Sci-Fi | 8.8 | 5111 | Series | 2017 | 2025 |
| 1999 | Comic Book Men | NaN | 22 min | Comedy, Reality-TV | 7.7 | 5109 | Series | 2012 | 2018 |
2000 rows × 9 columns
all_info = film_imdb_data.append(series_data, ignore_index=True, sort=False)
all_info.drop_duplicates(subset ='Name', keep = 'first', inplace = True)
all_info = all_info.reset_index()
all_info.drop(['index'], axis = 1, inplace = True)
all_info
| Name | start_date | rate | n_of_votes | genres | duration | type | certificate | end_date | episodes_runtime | |
|---|---|---|---|---|---|---|---|---|---|---|
| 0 | No Time to Die | 2021 | 7.6 | 107163 | Action, Adventure, Thriller | 163 | Film | PG-13 | 2021 | NaN |
| 1 | The Guilty | 2021 | 6.3 | 64375 | Crime, Drama, Thriller | 90 | Film | R | 2021 | NaN |
| 2 | The Many Saints of Newark | 2021 | 6.4 | 27145 | Crime, Drama | 120 | Film | R | 2021 | NaN |
| 3 | Venom: Let There Be Carnage | 2021 | 6.4 | 30443 | Action, Adventure, Sci-Fi | 97 | Film | PG-13 | 2021 | NaN |
| 4 | Dune | 2021 | 8.3 | 84636 | Action, Adventure, Drama | 155 | Film | PG-13 | 2021 | NaN |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 5214 | Shaman Kingu | 2001 | 8.1 | 5131 | Animation, Action, Adventure | NaN | Series | NaN | 2005 | 23 min |
| 5215 | Eerie, Indiana | 1991 | 8.2 | 5128 | Adventure, Comedy, Drama | NaN | Series | NaN | 1992 | 30 min |
| 5216 | Gunsmoke | 1955 | 7.9 | 5115 | Western | NaN | Series | NaN | 1975 | 60 min |
| 5217 | The Cheat | 2017 | 8.8 | 5111 | Action, Drama, Sci-Fi | NaN | Series | NaN | 2025 | 20 min |
| 5218 | Comic Book Men | 2012 | 7.7 | 5109 | Comedy, Reality-TV | NaN | Series | NaN | 2018 | 22 min |
5219 rows × 10 columns
alt.data_transformers.disable_max_rows()
DataTransformerRegistry.enable('default')
all_info_copy = all_info.copy(deep=False)
list_of_genres = all_info_copy['genres'].to_list()
unique_genres = []
for genres in list_of_genres:
lst = genres.split(',')
for element in lst:
element = element.replace(' ', '')
if element not in unique_genres:
unique_genres.append(element)
unique_genres
['Action', 'Adventure', 'Thriller', 'Crime', 'Drama', 'Sci-Fi', 'Comedy', 'History', 'Fantasy', 'Horror', 'Mystery', 'Animation', 'Family', 'Romance', 'Western', 'Musical', 'Biography', 'Music', 'War', 'Short', 'Sport', 'Film-Noir', 'Documentary', 'Reality-TV', 'News', 'Talk-Show', 'Game-Show']
action = all_info_copy[(all_info_copy['genres'].str.contains('Action'))].reset_index()
action.drop(['index'], axis = 1, inplace = True)
action['genre'] = 'Action'
len(action)
1374
adventure = all_info_copy[(all_info_copy['genres'].str.contains('Adventure'))].reset_index()
adventure.drop(['index'], axis = 1, inplace = True)
adventure['genre'] = 'Adventure'
len(adventure)
1084
thriller = all_info_copy[(all_info_copy['genres'].str.contains('Thriller'))].reset_index()
thriller.drop(['index'], axis = 1, inplace = True)
thriller['genre'] = 'Thriller'
len(thriller)
805
crime = all_info_copy[(all_info_copy['genres'].str.contains('Crime'))].reset_index()
crime.drop(['index'], axis = 1, inplace = True)
crime['genre'] = 'Crime'
len(crime)
968
drama = all_info_copy[(all_info_copy['genres'].str.contains('Drama'))].reset_index()
drama.drop(['index'], axis = 1, inplace = True)
drama['genre'] = 'Drama'
len(drama)
2657
sci_fi = all_info_copy[(all_info_copy['genres'].str.contains('Sci-Fi'))].reset_index()
sci_fi.drop(['index'], axis = 1, inplace = True)
sci_fi['genre'] = 'Sci-Fi'
len(sci_fi)
497
comedy = all_info_copy[(all_info_copy['genres'].str.contains('Comedy'))].reset_index()
comedy.drop(['index'], axis = 1, inplace = True)
comedy['genre'] = 'Comedy'
len(comedy)
1837
history = all_info_copy[(all_info_copy['genres'].str.contains('History'))].reset_index()
history.drop(['index'], axis = 1, inplace = True)
history['genre'] = 'History'
len(history)
143
fantasy = all_info_copy[(all_info_copy['genres'].str.contains('Fantasy'))].reset_index()
fantasy.drop(['index'], axis = 1, inplace = True)
fantasy['genre'] = 'Fantasy'
len(fantasy)
478
horror = all_info_copy[(all_info_copy['genres'].str.contains('Horror'))].reset_index()
horror.drop(['index'], axis = 1, inplace = True)
horror['genre'] = 'Horror'
len(horror)
664
mystery = all_info_copy[(all_info_copy['genres'].str.contains('Mystery'))].reset_index()
mystery.drop(['index'], axis = 1, inplace = True)
mystery['genre'] = 'Mystery'
len(mystery)
623
animation = all_info_copy[(all_info_copy['genres'].str.contains('Animation'))].reset_index()
animation.drop(['index'], axis = 1, inplace = True)
animation['genre'] = 'Animation'
len(animation)
464
family = all_info_copy[(all_info_copy['genres'].str.contains('Family'))].reset_index()
family.drop(['index'], axis = 1, inplace = True)
family['genre'] = 'Family'
len(family)
306
romance = all_info_copy[(all_info_copy['genres'].str.contains('Romance'))].reset_index()
romance.drop(['index'], axis = 1, inplace = True)
romance['genre'] = 'Romance'
len(romance)
608
western = all_info_copy[(all_info_copy['genres'].str.contains('Western'))].reset_index()
western.drop(['index'], axis = 1, inplace = True)
western['genre'] = 'Western'
len(western)
48
musical = all_info_copy[(all_info_copy['genres'].str.contains('Musical'))].reset_index()
musical.drop(['index'], axis = 1, inplace = True)
musical['genre'] = 'Musical'
len(musical)
43
biography = all_info_copy[(all_info_copy['genres'].str.contains('Biography'))].reset_index()
biography.drop(['index'], axis = 1, inplace = True)
biography['genre'] = 'Biography'
len(biography)
234
music = all_info_copy[(all_info_copy['genres'].str.contains('Music'))].reset_index()
music.drop(['index'], axis = 1, inplace = True)
music['genre'] = 'Music'
len(music)
148
war = all_info_copy[(all_info_copy['genres'].str.contains('War'))].reset_index()
war.drop(['index'], axis = 1, inplace = True)
war['genre'] = 'War'
len(war)
54
short = all_info_copy[(all_info_copy['genres'].str.contains('Short'))].reset_index()
short.drop(['index'], axis = 1, inplace = True)
short['genre'] = 'Short'
len(short)
38
sport = all_info_copy[(all_info_copy['genres'].str.contains('Sport'))].reset_index()
sport.drop(['index'], axis = 1, inplace = True)
sport['genre'] = 'Sport'
len(sport)
73
film_noir = all_info_copy[(all_info_copy['genres'].str.contains('Film-Noir'))].reset_index()
film_noir.drop(['index'], axis = 1, inplace = True)
film_noir['genre'] = 'Film-Noir'
len(film_noir)
5
documentary = all_info_copy[(all_info_copy['genres'].str.contains('Documentary'))].reset_index()
documentary.drop(['index'], axis = 1, inplace = True)
documentary['genre'] = 'Documentary'
len(documentary)
37
reality_TV = all_info_copy[(all_info_copy['genres'].str.contains('Reality-TV'))].reset_index()
reality_TV.drop(['index'], axis = 1, inplace = True)
reality_TV['genre'] = 'Reality-TV'
len(reality_TV)
56
news = all_info_copy[(all_info_copy['genres'].str.contains('News'))].reset_index()
news.drop(['index'], axis = 1, inplace = True)
news['genre'] = 'News'
len(news)
8
talk_show = all_info_copy[(all_info_copy['genres'].str.contains('Talk-Show'))].reset_index()
talk_show.drop(['index'], axis = 1, inplace = True)
talk_show['genre'] = 'Talk-Show'
len(talk_show)
33
game_show = all_info_copy[(all_info_copy['genres'].str.contains('Game-Show'))].reset_index()
game_show.drop(['index'], axis = 1, inplace = True)
game_show['genre'] = 'Game-Show'
len(game_show)
29
all_info_by_genre = action.append([adventure, thriller, crime, drama, sci_fi, comedy, history, fantasy, horror, mystery, animation,
family, romance, western, musical, biography, music, war, short, sport, film_noir, documentary, reality_TV,
news, talk_show, game_show], ignore_index=True, sort=False)
all_info_by_genre
| Name | start_date | rate | n_of_votes | genres | duration | type | certificate | end_date | episodes_runtime | genre | |
|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | No Time to Die | 2021 | 7.6 | 107163 | Action, Adventure, Thriller | 163 | Film | PG-13 | 2021 | NaN | Action |
| 1 | Venom: Let There Be Carnage | 2021 | 6.4 | 30443 | Action, Adventure, Sci-Fi | 97 | Film | PG-13 | 2021 | NaN | Action |
| 2 | Dune | 2021 | 8.3 | 84636 | Action, Adventure, Drama | 155 | Film | PG-13 | 2021 | NaN | Action |
| 3 | Free Guy | 2021 | 7.3 | 153835 | Action, Adventure, Comedy | 115 | Film | PG-13 | 2021 | NaN | Action |
| 4 | Black Widow | 2021 | 6.8 | 246603 | Action, Adventure, Sci-Fi | 134 | Film | PG-13 | 2021 | NaN | Action |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 13309 | Face Off | 2011 | 8.2 | 6079 | Game-Show, Reality-TV | NaN | Series | NaN | 2018 | 44 min | Game-Show |
| 13310 | RuPaul's Drag Race All Stars | 2012 | 8.5 | 5915 | Game-Show, Reality-TV | NaN | Series | NaN | 2025 | 60 min | Game-Show |
| 13311 | Top Chef | 2006 | 7.6 | 5503 | Game-Show, Reality-TV | NaN | Series | NaN | 2025 | 44 min | Game-Show |
| 13312 | 8 Out of 10 Cats | 2005 | 7.7 | 5287 | Comedy, Game-Show, News | NaN | Series | NaN | 2025 | 24 min | Game-Show |
| 13313 | Never Mind the Buzzcocks | 1996 | 7.8 | 5233 | Comedy, Game-Show, Music | NaN | Series | NaN | 2015 | 30 min | Game-Show |
13314 rows × 11 columns
def reduce(data, number=5):
new_data = []
for year in range(1922, 2022):
data_year_film = data[(data['type'] == 'Film') & (data['start_date'] == year)].reset_index()
data_year_film.drop(['index'], axis = 1, inplace = True)
if len(data_year_film) > (number * 2):
data_year_film = data_year_film.sort_values(by='n_of_votes', ascending=False)
data_year_film = data_year_film.head(len(data_year_film)//2)
data_year_film = data_year_film.sample(n = number)
elif len(data_year_film) > number:
data_year_film = data_year_film.sample(n = number)
new_data.append(data_year_film)
for year in range(1922, 2022):
data_year_series = data[(data['type'] == 'Series') & (data['start_date'] == year)].reset_index()
data_year_series.drop(['index'], axis = 1, inplace = True)
if len(data_year_series) > (number * 2):
data_year_series = data_year_series.sort_values(by='n_of_votes', ascending=False)
data_year_series = data_year_series.head(len(data_year_series)//2)
data_year_series = data_year_series.sample(n = number)
elif len(data_year_series) > number:
data_year_series = data_year_series.sample(n = number)
new_data.append(data_year_series)
return pd.concat(new_data)
reduced_info_by_genre = history.append([reduce(action.copy(deep=False)), reduce(adventure.copy(deep=False)), reduce(thriller.copy(deep=False)), reduce(crime.copy(deep=False)), reduce(drama.copy(deep=False)), reduce(sci_fi.copy(deep=False)), reduce(comedy.copy(deep=False)), reduce(fantasy.copy(deep=False)), reduce(horror.copy(deep=False)), reduce(mystery.copy(deep=False)), reduce(animation.copy(deep=False)),
family, reduce(romance.copy(deep=False)), western, musical, reduce(biography.copy(deep=False)), music, war, short, sport, film_noir, documentary, reality_TV,
news, talk_show, game_show], ignore_index=True, sort=False)
reduced_info_by_genre
| Name | start_date | rate | n_of_votes | genres | duration | type | certificate | end_date | episodes_runtime | genre | |
|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | The Last Duel | 2021 | 7.7 | 6498 | Action, Drama, History | 152 | Film | R | 2021 | NaN | History |
| 1 | The Courier | 2020 | 7.1 | 36654 | Drama, History, Thriller | 112 | Film | PG-13 | 2020 | NaN | History |
| 2 | The Eyes of Tammy Faye | 2021 | 7.0 | 1776 | Biography, Drama, History | 126 | Film | PG-13 | 2021 | NaN | History |
| 3 | The Tragedy of Macbeth | 2021 | 7.6 | 125 | Drama, History, Thriller | 105 | Film | R | 2021 | NaN | History |
| 4 | Schindler's List | 1993 | 8.9 | 1270601 | Biography, Drama, History | 195 | Film | R | 1993 | NaN | History |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 5539 | Face Off | 2011 | 8.2 | 6079 | Game-Show, Reality-TV | NaN | Series | NaN | 2018 | 44 min | Game-Show |
| 5540 | RuPaul's Drag Race All Stars | 2012 | 8.5 | 5915 | Game-Show, Reality-TV | NaN | Series | NaN | 2025 | 60 min | Game-Show |
| 5541 | Top Chef | 2006 | 7.6 | 5503 | Game-Show, Reality-TV | NaN | Series | NaN | 2025 | 44 min | Game-Show |
| 5542 | 8 Out of 10 Cats | 2005 | 7.7 | 5287 | Comedy, Game-Show, News | NaN | Series | NaN | 2025 | 24 min | Game-Show |
| 5543 | Never Mind the Buzzcocks | 1996 | 7.8 | 5233 | Comedy, Game-Show, Music | NaN | Series | NaN | 2015 | 30 min | Game-Show |
5544 rows × 11 columns
film1 = alt.Chart(all_info).mark_circle(color = "#B0B0B0").encode(
x = alt.X('rate:Q', axis=alt.Axis(title = "Рейтинг")),
y = alt.Y('duration:Q', axis=alt.Axis(title = "Тривалість у хвилинах")),
tooltip = [
alt.Tooltip(title = 'Name', field = 'Name', type = 'nominal'),
alt.Tooltip(title = 'Date', field = 'start_date', type = 'quantitative'),
alt.Tooltip(title = 'Genre(s)', field = 'genres',type = 'nominal'),
alt.Tooltip(title = 'IMDB Rate', field='rate', type = 'quantitative'),
alt.Tooltip(title = 'Number of votes', field='n_of_votes', type = 'quantitative')
]
).transform_filter(
alt.FieldEqualPredicate(field='type', equal='Film')
)
film2 = alt.Chart(all_info).mark_circle(color = "black").encode(
x = alt.X('rate:Q', ),
y = alt.Y('duration:Q'),
tooltip = [
alt.Tooltip(title = 'Name', field = 'Name', type = 'nominal'),
alt.Tooltip(title = 'Date', field = 'start_date', type = 'quantitative'),
alt.Tooltip(title = 'Genre(s)', field = 'genres',type = 'nominal'),
alt.Tooltip(title = 'IMDB Rate', field='rate', type = 'quantitative'),
alt.Tooltip(title = 'Number of votes', field='n_of_votes', type = 'quantitative')
]
).transform_filter(
alt.FieldEqualPredicate(field='type', equal='Film')
).transform_filter(
alt.FieldRangePredicate(field='rate', range=[7, 9])
).transform_filter(
alt.FieldRangePredicate(field='duration', range=[80, 140]))
area1 = pd.DataFrame.from_dict({'x': [0], 'x1': [10], 'y': [80], 'y1': [140]})
horizontal = alt.Chart(area1).mark_rect(opacity = 0.1, fill = 'blue').encode(
x = alt.X('x:Q'),
y = alt.Y('y:Q'),
x2 = alt.X2('x1:Q'),
y2 = alt.Y2('y1:Q')
)
area2 = pd.DataFrame.from_dict({'x': [7], 'x1': [9], 'y': [0], 'y1': [260]})
vertical = alt.Chart(area2).mark_rect(opacity = 0.1, fill = 'red').encode(
x = alt.X('x:Q'),
y = alt.Y('y:Q'),
x2 = alt.X2('x1:Q'),
y2 = alt.Y2('y1:Q')
)
film3 = alt.Chart(all_info).mark_point(color = "black").encode(
x = alt.X('rate:Q'
, axis=alt.Axis(title = "Рейтинг")
, scale = alt.Scale(domain = [7, 9], nice = False)),
y = alt.Y('duration:Q'
, axis=alt.Axis(title = "")
, scale = alt.Scale(domain = [80, 140], nice = False)),
size = alt.Size('n_of_votes:Q', legend = alt.Legend(
title = "Кількість голосів",
clipHeight=20,
format = '.2s')),
tooltip = [
alt.Tooltip(title = 'Name', field = 'Name', type = 'nominal'),
alt.Tooltip(title = 'Date', field = 'start_date', type = 'quantitative'),
alt.Tooltip(title = 'Genre(s)', field = 'genres',type = 'nominal'),
alt.Tooltip(title = 'IMDB Rate', field='rate', type = 'quantitative'),
alt.Tooltip(title = 'Number of votes', field='n_of_votes', type = 'quantitative')
]
).transform_filter(
alt.FieldEqualPredicate(field='type', equal='Film')
).transform_filter(
alt.FieldRangePredicate(field='rate', range=[7, 9])
).transform_filter(
alt.FieldRangePredicate(field='duration', range=[80, 140])
).transform_filter(
alt.FieldOneOfPredicate(field='certificate', oneOf=['G', 'PG', 'PG-13', 'NC-17', 'R'])
)
film_duration = (horizontal + vertical + film1 + film2).properties(width = 400, height = 700)
chart_film_duration = alt.hconcat(film_duration,
film3.properties(width = 400, height = 700).interactive()
).properties(title = alt.TitleParams(
text = 'Фільми: Тривалість - Рейтинг',
subtitle = 'графік із інтерактивним полем для дослідження інтервалу 80-140хв (Тривалість) та 7-9 (Рейтинг)')
).configure_title(anchor = 'start',
frame = 'group',
fontSize = 24,
subtitleFontSize = 16).configure_axis(
domain = False,
ticks = False
)
dropdown = alt.binding_select(options = all_info_by_genre.genre.unique())
select_genre = alt.selection_single(empty = 'none', bind = dropdown, fields = ['genre'], init={'genre': 'Action'})
chart = alt.Chart(reduced_info_by_genre).mark_point().encode(
y = alt.Y('rate:Q', axis=alt.Axis(format = '1', title = "Рейтинг"), scale = alt.Scale(domain = [0, 10])),
x = alt.X('start_date:Q', axis=alt.Axis(format = '1', title = "Рік випуску"), scale = alt.Scale(domain = [1920, 2025], nice = False)),
detail = alt.Detail('genre:N'),
size = alt.Size('n_of_votes:Q',
scale = alt.Scale(
range = [10, 1000]
),
legend = alt.Legend(
title = "Кількість голосів",
clipHeight=30,
format = '.2s')),
color = alt.Color('type:N', scale = alt.Scale(domain = ['Film', 'Series'], range = ['#005EFF', '#FD7F20']), legend=alt.Legend(title='Тип')),
tooltip = alt.Tooltip('Name')
)
chart_by_genres = chart.properties(width = 900, height = 700, background = '#F5F5F5'
).add_selection(select_genre).transform_filter(select_genre).properties(
title = alt.TitleParams(
text = 'Розподіл фільмів та серіалів, рік випуску - рейтинг IMDB',
subtitle = 'з можливістю вибрати жанр')
).configure_title(anchor = 'start',
frame = 'group',
fontSize = 24,
subtitleFontSize = 16).interactive()
year_brush = alt.selection_interval(encodings=['x'])
rate_brush = alt.selection_interval(encodings=['x'])
bars = alt.Chart(all_info_by_genre).mark_bar(color = '#7EC8E3').encode(
x = alt.X('count():Q', scale = alt.Scale(domain = [0, 1600])),
y = alt.Y('genre:O', sort='-x'),
tooltip = alt.Tooltip('count()'),
)
text = alt.Chart(all_info_by_genre).mark_text(dx=15, dy=3, fontSize = 13, fontWeight=600, color='black').encode(
x = alt.X('count():Q', scale = alt.Scale(nice = False)),
y = alt.Y('genre:O', sort='-x'),
text=alt.Text('count():Q'),
)
chart = (bars + text)
interval_year = alt.Chart(all_info_by_genre).mark_bar().encode(
x = alt.X('start_date:Q', axis=alt.Axis(format = '1', title = "Розподіл фільмів-серіалів за роками"), scale = alt.Scale(domain = [1920, 2022], nice = False)),
y = alt.Y('count():Q'),
color = alt.Color('type:N')
)
interval_rate = alt.Chart(all_info_by_genre).mark_bar().encode(
x = alt.X('rate:Q', axis=alt.Axis(format = '1', title = "Розподіл фільмів-серіалів за рейтингом"), scale = alt.Scale(domain = [0, 10], nice = False)),
y = alt.Y('count():Q'),
color = alt.Color('type:N')
)
number_of_films_and_series = alt.vconcat(
chart.properties(width = 800/2, height = 700).transform_filter(year_brush).transform_filter(
rate_brush).facet(facet = alt.Facet('type:N', title=None), columns = 2),
interval_year.properties(width = 800, height = 100).add_selection(year_brush),
Тривалість у хвилинах
interval_rate.properties(width = 800, height = 100).add_selection(rate_brush)
).properties(title = alt.TitleParams(
text = 'Кількість фільмів та серіалів певного жанру',
subtitle = 'з можливістю визначити інтервали для років та рейтингу IMDB')
).configure_title(anchor = 'start',
frame = 'group',
fontSize = 24,
subtitleFontSize = 16).configure_axis(
domain = False,
ticks = False,
title = None
).configure_legend(orient='bottom', title = None)
Мені було цікаво чи є залежність між тривалістю фільму та його оцінкою(рейтиргом), тому я вирішила візуалізувати цей графік, як Scatter.
Оскільки ця задача належить до категорій Розподіл та Кореляція, то альтернативними варіантами могли б бути Heatmap, Correlogram та інші. Я обрала Scatter, оскільки таким чином можна наочно побачити кількість фільмів на графіку.
Крім цього, я виділила інтервал 80-140хв (Тривалість) та 7-9 (Рейтинг) та відобразила його на збільшеному графіку збоку, який можна зумити та дізнатися, які саме фільми в нього потрапили. Також на ньому можна побачити не тільки рейтинг цих фільмів, але й кількість голосів.
Недоліком такого типу представлення може бути хіба що те, що з нього не одразу можна сказати чи корелють ці два показники, а також він може здатися трохи шумним, але для того, щоб трохи нейтралізувати цей недолік, я зробила виділення певного інтервалу та додала його збільшену копію.
З цього графіку можу зробити висновок, що більшість фільмів були зняти в проміжку 80-140хв, а також, що переважна більшість фільми, які тривають довше 140 хвилин мають рейтинг 5 та більше.
chart_film_duration.display(actions = False, renderer = 'png')
Наступний графік я хотіла створити для того, щоб побачити розподіл фільмів та серіалів в розрізі років та рейтингу.
Я обрала спосіб схожий до попереднього графіку, проте тут я виділяю тип (Фільм, Серіал) кольором та показую кількість голосів розміром, також я додала дропдаун селектор з жанрами для того, щоб можнабуло оцінити фільми та серіали на графіку в зажежності від їхнього жанру та зробити висновки, а також це дозволило не перевантажувати один графік інформацією.
Альтернативи та недоліки такі ж, як і на минулому графіку.
Також я додала інтерактивність для цього графіку, тобто його можна зумити та якщо навсети мишку на кульку то можна буде дізнатися назву фільму або серіалу.
З цього графіку я можу зробити певні висновки, наприклад, що в деяких жанрах переважають Фільми, а в інших - Серіали, а також, що Серіали в деяких жанрах переважно отримують вищі оцінки ніж, Фільми, проте за фільми переважного голосує більше людей, ніж за серіали. І загалом, можна оцінити фільми та серіали різного жанру впродовж всього часу, те коли почали з'являтися Фільми та Серіали певного жанру та те, як їх оцінили в залежності від року випуску.
chart_by_genres.display(actions = False, renderer = 'png')
Цей графік мав показати кількість фільмів та серіалів в розрізі жанрів.
Я розглядала також схожий до цього спосіб представлення, який відрізнявся тільки тим, що тип(Фільм, Серіал) позначався кольором, проте цей варіант має великий недолік. Через те, що в деяких жанрах дуже мало фільмів та/чи серіалів, їх просто не було б видно, а зменшити скейл графіку я не можу через велику різницю в кількості серед жанрів. Між іншим, мальньку копію схожого графіку таким способом можна побачити чуть нижче, вона також слугує селектором, тобто з її допомогою можна вибрати інтервал років за які потрібно показати кількість серіалів та фільмів. Крім цього я додала, ще один селектор, який дозволяє вибрати інтервал рейтингу і так само, як і графік над ним - впливає на основний графік, відсіюючи фільми та серіали, які не потрапляють до інтервалу.
За замовчуванням, графік показує кількість фільмів та серіалів за весь час та всіх рейтингів.
З цього графіку я можу зробити такі висновки:
І цей список можна продовжувати, якщо використати фільтри-селектори.
number_of_films_and_series.display(actions = False, renderer = 'png')